/*******************************************
 * Copyright 2007 Aphrodite Framework for javaME .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * Email : owner@aphroditeme.dev.java.net
 * Contributors:
 *     Kent Elton - initial API and implementation
 *******************************************/

package com.aphrodite.io;

/**
 * Clean-room implementation of ByteBuffer
 * 
 */
public final class ByteBuffer extends Buffer {

	public static ByteBuffer allocate(int capacity) {
		return new ByteBuffer(new byte[capacity]);
	}

	public static ByteBuffer allocateDirect(int capacity) {
		return new ByteBuffer(new byte[capacity]);
	}

	final public static ByteBuffer wrap(byte[] array) {
		return new ByteBuffer(array);
	}

	private final byte[] _bytes;

	private ByteOrder _order = ByteOrder.BIG_ENDIAN;

	private ByteBuffer(byte[] bytes) {
		super(bytes.length, bytes.length, 0, -1);
		_bytes = bytes;
	}

	public final byte[] array() {
		return _bytes;
	}

	public final int arrayOffset() {
		return 0;
	}

	public byte get() {
		return _bytes[_position++];
	}

	public ByteBuffer get(byte[] dst) {
		return get(dst, 0, dst.length);
	}

	public ByteBuffer get(byte[] dst, int offset, int length) {
		for (int i = offset; i < offset + length; i++) {
			dst[i] = get();
		}

		return this;
	}

	public byte get(int index) {
		return _bytes[index];
	}

	public char getChar() {
		return getChar(_position++);
	}

	public char getChar(int index) {
		return (char) getShort(index);
	}

	public int getInt() {
		return getInt(_position++);
	}

	public int getInt(int index) {
		if (_order == ByteOrder.LITTLE_ENDIAN) {
			return (_bytes[index] & 0xff) + ((_bytes[++index] & 0xff) << 8) + ((_bytes[++index] & 0xff) << 16)
					+ ((_bytes[++index] & 0xff) << 24);
		} else {
			return (_bytes[index] << 24) + ((_bytes[++index] & 0xff) << 16) + ((_bytes[++index] & 0xff) << 8)
					+ (_bytes[++index] & 0xff);
		}
	}

	public long getLong() {
		return getLong(_position++);
	}

	public long getLong(int index) {
		if (_order == ByteOrder.LITTLE_ENDIAN) {
			return (_bytes[index] & 0xff) + ((_bytes[++index] & 0xff) << 8) + ((_bytes[++index] & 0xff) << 16)
					+ ((_bytes[++index] & 0xffL) << 24) + ((_bytes[++index] & 0xffL) << 32)
					+ ((_bytes[++index] & 0xffL) << 40) + ((_bytes[++index] & 0xffL) << 48)
					+ (((long) _bytes[++index]) << 56);
		} else {
			return (((long) _bytes[index]) << 56) + ((_bytes[++index] & 0xffL) << 48)
					+ ((_bytes[++index] & 0xffL) << 40) + ((_bytes[++index] & 0xffL) << 32)
					+ ((_bytes[++index] & 0xffL) << 24) + ((_bytes[++index] & 0xff) << 16)
					+ ((_bytes[++index] & 0xff) << 8) + (_bytes[++index] & 0xffL);
		}
	}

	public short getShort() {
		return getShort(_position++);
	}

	public short getShort(int index) {
		if (_order == ByteOrder.LITTLE_ENDIAN) {
			return (short) ((_bytes[index] & 0xff) + (_bytes[++index] << 8));
		} else {
			return (short) ((_bytes[index] << 8) + (_bytes[++index] & 0xff));
		}
	}

	public final boolean hasArray() {
		return true;
	}

	public boolean isDirect() {
		return false;
	}

	public final ByteOrder order() {
		return _order;
	}

	public final ByteBuffer order(ByteOrder endian) {
		_order = endian;
		return this;
	}

	public ByteBuffer put(byte b) {
		_bytes[_position++] = b;
		return this;
	}

	public final ByteBuffer put(byte[] src) {
		return put(src, 0, src.length);
	}

	public ByteBuffer put(byte[] src, int offset, int length) {
		for (int i = offset; i < offset + length; i++)
			put(src[i]);

		return this;
	}

	public ByteBuffer put(ByteBuffer src) {
		if (src.remaining() > 0) {
			byte[] toPut = new byte[src.remaining()];
			src.get(toPut);
			src.put(toPut);
		}

		return this;
	}

	public ByteBuffer put(int index, byte b) {
		_bytes[index] = b;
		return this;
	}

	public ByteBuffer putChar(char value) {
		putChar(_position++, value);
		return this;
	}

	public ByteBuffer putChar(int index, char value) {
		return putShort(index, (short) value);
	}

	public ByteBuffer putInt(int value) {
		return putInt(_position++, value);
	}

	public ByteBuffer putInt(int index, int value) {
		if (_order == ByteOrder.LITTLE_ENDIAN) {
			_bytes[index] = (byte) value;
			_bytes[++index] = (byte) (value >> 8);
			_bytes[++index] = (byte) (value >> 16);
			_bytes[++index] = (byte) (value >> 24);
		} else {
			_bytes[index] = (byte) (value >> 24);
			_bytes[++index] = (byte) (value >> 16);
			_bytes[++index] = (byte) (value >> 8);
			_bytes[++index] = (byte) value;
		}
		return this;
	}

	public ByteBuffer putLong(int index, long value) {
		if (_order == ByteOrder.LITTLE_ENDIAN) {
			_bytes[index] = (byte) value;
			_bytes[++index] = (byte) (value >> 8);
			_bytes[++index] = (byte) (value >> 16);
			_bytes[++index] = (byte) (value >> 24);
			_bytes[++index] = (byte) (value >> 32);
			_bytes[++index] = (byte) (value >> 40);
			_bytes[++index] = (byte) (value >> 48);
			_bytes[++index] = (byte) (value >> 56);
		} else {
			_bytes[index] = (byte) (value >> 56);
			_bytes[++index] = (byte) (value >> 48);
			_bytes[++index] = (byte) (value >> 40);
			_bytes[++index] = (byte) (value >> 32);
			_bytes[++index] = (byte) (value >> 24);
			_bytes[++index] = (byte) (value >> 16);
			_bytes[++index] = (byte) (value >> 8);
			_bytes[++index] = (byte) value;
		}
		return this;
	}

	public ByteBuffer putLong(long value) {
		return putLong(_position++, value);
	}

	public ByteBuffer putShort(int index, short value) {
		if (_order == ByteOrder.LITTLE_ENDIAN) {
			_bytes[index] = (byte) value;
			_bytes[++index] = (byte) (value >> 8);
		} else {
			_bytes[index] = (byte) (value >> 8);
			_bytes[++index] = (byte) value;
		}
		return this;
	}

	public ByteBuffer putShort(short value) {
		return putShort(_position++, value);
	}

	/*
	 * @JVM-1.1+@
	 * 
	 * public float getFloat() { return getFloat(_position++); }
	 * 
	 * public ByteBuffer putFloat(float value) { return putFloat(_position++,
	 * value); }
	 * 
	 * public float getFloat(int index) { return
	 * Float.intBitsToFloat(getInt(index)); }
	 * 
	 * public ByteBuffer putFloat(int index, float value) { return putInt(index,
	 * Float.floatToIntBits(value)); }
	 * 
	 * public double getDouble() { return getDouble(_position++); }
	 * 
	 * public ByteBuffer putDouble(double value) { return putDouble(_position++,
	 * value); }
	 * 
	 * public double getDouble(int index) { return
	 * Double.longBitsToDouble(getLong(index)); }
	 * 
	 * public ByteBuffer putDouble(int index, double value) { return
	 * putLong(index, Double.doubleToLongBits(value)); }
	 * 
	 * /
	 */
}
